/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdexcept>
#include <QDir>
#include <QCryptographicHash>
#include <QSettings>
#include "imagestorage.h"
#include "log.h"
#include "paths.h"

namespace Wishmaster
{
	ImageStorage::ImageStorage()
	{
		m_root = DefaultPath::storageRoot() + "/db";
		m_picroot = m_root + QDir::separator() + "pics";
		QDir dir;
		if(!dir.exists(m_root))
			dir.mkpath(m_root);

		m_sqlite.open((m_root + "/images.db").toUtf8().data());
		makeDB();
		log(llDebug, "Image database opened successfully");
	}

	ImageStorage::~ImageStorage()
	{
	}

	bool ImageStorage::makeDB()
	{
		m_sqlite.execDML("CREATE TABLE IF NOT EXISTS images (id INTEGER, path STRING, hash VARCHAR(32), type VARCHAR(8), PRIMARY KEY(id))");
		m_sqlite.execDML("CREATE TABLE IF NOT EXISTS tags (id INTEGER, name VARCHAR(40), PRIMARY KEY(id))");
		m_sqlite.execDML("CREATE TABLE IF NOT EXISTS links (id INTEGER, image_id INTEGER, tag VARCHAR(40), PRIMARY KEY(id))");
		return true;
	}

	int ImageStorage::getIDByHash(const QByteArray& hash)
	{
		QString sql = QString("SELECT * FROM images WHERE hash=\'%1\'").arg(QString(hash.toHex()));
		//log(llDebug, "Query: %s", sql.toAscii().data());
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		if(q.eof())
			return 0;
		
		return q.getIntField(0);
	}

	int ImageStorage::getIDByPath(const QString& path)
	{
		QString sql = QString("SELECT * FROM images WHERE path=\'%1\'").arg(path);
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		if(q.eof())
			return 0;
		
		return q.getIntField(0);
	}

	int ImageStorage::addImage(QImage pixmap, QString const& name)
	{
		if(pixmap.isNull())
			return 0;
		return addImage(pixmap, "", name);
	}

	int ImageStorage::addImage(QByteArray arr, QString const& name)
	{
		return addImage(arr, "", name);
	}

	QString ImageStorage::makePicPath(const QString& initialPath, const QByteArray& hash)
	{
		QString this_path = initialPath;
		if(this_path.isEmpty())
			this_path = QString(hash.left(1).toHex()) + QDir::separator();
		this_path.prepend("db/pics/");

		QDir d(DefaultPath::storageRoot() + QDir::separator() + this_path);
		if(!d.exists())
			d.mkpath(".");
		return this_path;
	}

	bool ImageStorage::shouldBeSaved(const QByteArray& hash)
	{
		QSettings settings;
		if(settings.value("imagestorage/checkhash", true).toBool())
		{
			int id = getIDByHash(hash);
			if(id != 0)
			{
				log(llDebug, "Image already in DB");
				return false;
			}
		}
		return true;
	}

	int ImageStorage::addImage(QImage pixmap, const QString& path, const QString& name)
	{
		if(pixmap.isNull())
			return 0;

		QByteArray data((const char*)pixmap.bits(), pixmap.numBytes());
		QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5);

		QString this_path = makePicPath(path, hash);

		if(!shouldBeSaved(hash))
			return -1;
		pixmap.save(DefaultPath::storageRoot() + QDir::separator() + this_path + QDir::separator() + name); // FIXME hardcoded

		QString sql = QString("INSERT INTO images(path, hash) VALUES('%1', '%2')").arg(this_path + QDir::separator() + name).arg(QString(hash.toHex()));
		m_sqlite.execDML(sql.toUtf8().data());
		//log(llDebug, "Query: %s", sql.toAscii().data());
		return m_sqlite.lastRowId();
	}
	
	int ImageStorage::addImage(QByteArray arr, const QString& path, const QString& name)
	{
		QByteArray hash = QCryptographicHash::hash(arr, QCryptographicHash::Md5);

		QString this_path = makePicPath(path, hash);

		if(!shouldBeSaved(hash))
			return -1;
		
		QFile file(DefaultPath::storageRoot() + QDir::separator() + this_path + QDir::separator() + name);
		file.open(QIODevice::WriteOnly);
		file.write(arr);

		QString sql = QString("INSERT INTO images(path, hash) VALUES('%1', '%2')").arg(this_path + QDir::separator() + name).arg(QString(hash.toHex()));
		m_sqlite.execDML(sql.toUtf8().data());
		return m_sqlite.lastRowId();
	}

	bool ImageStorage::deleteImage(int id)
	{
		QString path = getPathForID(id);
		//log(llDebug, "PATH: %s", qPrintable(path));
		QFileInfo info(DefaultPath::storageRoot() + QDir::separator() + path);
		if(!info.dir().remove(info.fileName()))
		{
			log(llError, "Unable to delete file");
		}

		QString sql = QString("DELETE FROM images WHERE id='%1'").arg(id);
		m_sqlite.execDML(sql.toUtf8().data());
		return true;
	}

	void ImageStorage::clearTagsForID(int id)
	{
		QString sql = QString("DELETE FROM links WHERE image_id='%1'").arg(id);
		m_sqlite.execDML(sql.toUtf8().data());
	}

	void ImageStorage::addTagForID(int id, QString const& tag)
	{
		QString sql = QString("INSERT INTO links('image_id', 'tag') VALUES('%1', '%2')").arg(id).arg(tag);
		m_sqlite.execDML(sql.toUtf8().data());
	}

	QStringList ImageStorage::getTagsForID(int id)
	{
		QStringList tags;
		QString sql = QString("SELECT * FROM links WHERE image_id=\'%1\'").arg(id);
		//log(llDebug, "Query: %s", sql.toAscii().data());
		
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		while(!q.eof())
		{
			tags.append(QString::fromUtf8(q.getStringField(2)));
			q.nextRow();
		}

		return tags;
	}

	QStringList ImageStorage::getPathsForTags(QStringList const& tags)
	{
		QString sql;
		QStringList paths;
		if(tags.empty())
		{
			sql = "SELECT * FROM images";
		}
		else
		{
			sql = "SELECT * FROM images WHERE ";
			int i = 0;
			for(QStringList::const_iterator it = tags.begin(); it != tags.end(); ++it)
			{
				sql += QString("id IN (SELECT image_id FROM links WHERE tag='%1') ").arg(*it);
				if(i < tags.count() - 1)
				{
					sql += "AND ";
				}
				i++;
			}
		}
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		while(!q.eof())
		{
			paths.append(QString::fromUtf8(q.getStringField(1)));
			q.nextRow();
		}

		return paths;
	}

	QImage ImageStorage::getImage(int id)
	{
		QString path = getPathForID(id);
		if(!path.isEmpty())
		{
			QImage pic;
			pic.load(path);
			return pic;
		}
		return QImage();
	}

	QString ImageStorage::getPathForID(int id)
	{
		QString sql = QString("SELECT * FROM images WHERE id=\'%1\'").arg(id);

		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		if(q.eof())
			return QString::null;

		return DefaultPath::storageRoot() + QDir::separator() + q.getStringField(1);
	}

	QList<int> ImageStorage::getIDsForTags(QStringList const& tags)
	{
		QString sql;
		QList<int> ids;
		if(tags.empty())
		{
			sql = "SELECT * FROM images";
		}
		else
		{
			sql = "SELECT * FROM images WHERE ";
			int i = 0;
			for(QStringList::const_iterator it = tags.begin(); it != tags.end(); ++it)
			{
				sql += QString("id IN (SELECT image_id FROM links WHERE tag='%1') ").arg(*it);
				if(i < tags.count() - 1)
				{
					sql += "AND ";
				}
				i++;
			}
		}
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		while(!q.eof())
		{
			ids.append(q.getIntField(0));
			q.nextRow();
		}

		return ids;
	}

	QList<ImageDescriptor> ImageStorage::getImagesForTags(const QStringList& tags)
	{
		QString sql;
		QList<ImageDescriptor> descs;
		if(tags.empty())
		{
			sql = "SELECT * FROM images";
		}
		else
		{
			sql = "SELECT * FROM images WHERE ";
			int i = 0;
			for(QStringList::const_iterator it = tags.begin(); it != tags.end(); ++it)
			{
				sql += QString("id IN (SELECT image_id FROM links WHERE tag='%1') ").arg(*it);
				if(i < tags.count() - 1)
				{
					sql += "AND ";
				}
				i++;
			}
		}
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());

		while(!q.eof())
		{
			ImageDescriptor desc;
			desc.id = q.getIntField(0);
			desc.path = DefaultPath::storageRoot() + QDir::separator() + QString::fromUtf8(q.getStringField(1));
			descs.append(desc);
			q.nextRow();
		}
		return descs;
	}

	QString ImageStorage::getImageType(int id)
	{
		QString path = getPathForID(id);
		return path.right(path.size() - path.lastIndexOf('.'));
	}

	QList<TagInfo> ImageStorage::allTags()
	{
		QList<TagInfo> tags;
		QString sql = "SELECT tag,COUNT(*) FROM links GROUP BY tag";
		CppSQLite3Query q = m_sqlite.execQuery(sql.toUtf8().data());
		while(!q.eof())
		{
			QString tag = QString::fromUtf8(q.getStringField(0));
			if(tag.isEmpty())
			{
				q.nextRow();
				continue;
			}
			TagInfo info;
			info.tag = tag;
			info.images = q.getIntField(1);
			tags.append(info);
			q.nextRow();
		}
		return tags;
	}
}

